#include "world/agent.hpp"
#include "world/map.hpp"
#include "world/path.hpp"
#include "world/world.hpp"
#include <nlohmann/json.hpp>
#include <iostream>
using json = nlohmann::json;
using namespace std;
using namespace dw;

enum Format
{
    RESET,
    FONT_BLACK,
    FONT_RED,
    FONT_GREEN,
    FONT_YELLOW,
    FONT_WHITE,
    FONT_DEFAULT,
    BG_BLACK,
    BG_RED,
    BG_GREEN,
    BG_YELLOW,
    BG_WHITE,
    BG_DEFAULT,
};

const string format[] = {
    "\033[0m",
    "\033[30m",
    "\033[31m",
    "\033[32m",
    "\033[33m",
    "\033[37m",
    "\033[49m",
    "\033[40m",
    "\033[41m",
    "\033[42m",
    "\033[43m",
    "\033[47m",
    "\033[49m"
};

void title(const string &title, Format fmt = FONT_YELLOW)
{
    const size_t width = 80;
    const size_t len = title.length();

    cout << endl;
    cout << format[fmt] << setfill('=');

    cout << setw((width - len) / 2 - 1) << "";
    cout << " " << title << " ";
    cout << setw((width - len - 1) / 2) << "";

    cout << format[RESET] << setfill(' ') << endl;
    cout << endl;
}

int main() {
    title("Map");
    {
        Map map;
        Map::State state;
        json j;

        map.load("./test/MapData/perlin.txt");
        state = map.getMapState();

        cout << map << endl;

        j = state;
        cout << setw(4) << j << endl;
        j.get_to(state);
        cout << state << endl;

        j = map;
        cout << setw(4) << j << endl;
        j.get_to(map);
        cout << map << endl;
    }

    title("Agent");
    {
        Agent agent;
        Agent::State state;
        json j;

        state.id = 1000;
        state.shape = Agent::Shape::Circle;
        state.dRadius = 1.5;
        state.dVelocity = 0;
        state.dAcceleration = 1;
        state.dOmega = 2;
        state.vDirection = {-1, 0};
        state.vPosition = {3, 4};

        j = state;
        cout << setw(4) << j << endl;
        j.get_to(state);

        agent.set(state);
        j = agent;
        cout << setw(4) << j << endl;

        string str = agent.encode();
        agent.decode(str);
        j = agent;
        cout << setw(4) << j << endl;
    }

    title("Agent Manager");
    {
        AgentManager am;
        Agent::State state;
        json j;

        state.id = 1000;
        state.shape = Agent::Shape::Circle;
        state.dRadius = 1.5;
        state.dVelocity = 0;
        state.dAcceleration = 1;
        state.dOmega = 2;
        state.vDirection = {-1, 0};
        state.vPosition = {3, 4};

        am.CreateAgent(state);
        state.id = 999;
        state.vPosition = {5, 6};
        am.CreateAgent(state);

        to_json(j, am);
        cout << setw(4) << j << endl;
        from_json(j, am);
        to_json(j, am);
        cout << setw(4) << j << endl;
    }

    title("Path");
    {
        Map perlin;
        perlin.load("./test/MapData/perlin.txt");
        cout << "Perlin: Loaded from file" << endl;
        perlin.show();

        Path path;
        json j;

        Map::State state = perlin.getMapState();
        path(state, {0, 0}, {56, 30});
        showPathOnMap(perlin, path);

        to_json(j, path);
        cout << setw(4) << j << endl;

        Path p;
        from_json(j, p);
        showPathOnMap(perlin, p);
    }

    title("World");
    {
        World world("./test/MapData/perlin.txt");
        Map perlin = world.getMap();
        Map::State state = perlin.getMapState();
        perlin.show();

        shared_ptr<AutonomousAgent> agent1 = world.createAutonomousAgent();
        shared_ptr<Path> p1 = world.getPath(agent1->getID());

        shared_ptr<Agent> agent2 = world.createAgent();
        shared_ptr<Path> p2 = world.FindPath(agent2->getAgentState());

        showPathOnMap(perlin, *world.getPath(agent1->getID()));
        showPathOnMap(perlin, *world.getPath(agent2->getID()));

        showPathOnMap(perlin, *p1);
        p1->simplify(state);
        showPathOnMap(perlin, *p1);

        showPathOnMap(perlin, *p2);
        world.simplify(p2);
        showPathOnMap(perlin, *p2);

        json j;
        to_json(j, world);
        cout << setw(4) << j << endl;

        World w;
        from_json(j, w);
        to_json(j, w);
        cout << setw(4) << j << endl;
    }
    return 0;
}